﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Dynamic;
using System.Collections.Specialized;
using System.Reflection;
using System.Data;
using System.Data.Common;

namespace Framework.Extensions
{
    public static class Ext
    {
        [System.Diagnostics.DebuggerStepThrough]
        public static dynamic RecordToExpando(this IDataReader rdr)
        {
            dynamic e = new ExpandoObject();
            var d = e as IDictionary<string, object>;
            for (int i = 0; i < rdr.FieldCount; i++)
                d.Add(rdr.GetName(i), DBNull.Value.Equals(rdr[i]) ? null : rdr[i]);
            return e;
        }

        [System.Diagnostics.DebuggerStepThrough]
        public static void AddParams(this DbCommand cmd, object[] args)
        {
            foreach (var item in args)
            {
                AddParam(cmd, item);
            }
        }
        /// <summary>
        /// Extension for adding single parameter
        /// </summary>
        [System.Diagnostics.DebuggerStepThrough]
        public static void AddParam(this DbCommand cmd, object item)
        {
            var p = cmd.CreateParameter();
            p.ParameterName = string.Format("@{0}", cmd.Parameters.Count);
            if (item == null)
            {
                p.Value = DBNull.Value;
            }
            else
            {
                if (item.GetType() == typeof(Guid))
                {
                    p.Value = item.ToString();
                    p.DbType = DbType.String;
                    p.Size = 4000;
                }
                else if (item.GetType() == typeof(ExpandoObject))
                {
                    var d = (IDictionary<string, object>)item;
                    p.Value = d.Values.FirstOrDefault();
                }
                else
                {
                    p.Value = item;
                }
                //from DataChomp
                if (item.GetType() == typeof(string))
                    p.Size = 4000;
            }
            cmd.Parameters.Add(p);
        }

        [System.Diagnostics.DebuggerStepThrough]
        public static void ForEach(this int i, Action<int> doStuff)
        {
            for (int j = 0; j < i; ++j) doStuff(j);
        }

        [System.Diagnostics.DebuggerStepThrough]
        public static IEnumerable<T> Select<T>(this int i, Func<int, T> doStuff)
        {
            for (int j = 0; j < i; ++j) yield return doStuff(j);
        }

        [System.Diagnostics.DebuggerStepThrough]
        public static void ForEach<T>(this IEnumerable<T> list, Action<T> doStuff)
        {
            foreach (var item in list) doStuff(item);
        }

        [System.Diagnostics.DebuggerStepThrough]
        public static void ForEach<T>(this IEnumerable list, Action<T> doStuff)
        {
            foreach (var item in list) doStuff((T)item);
        }

        [System.Diagnostics.DebuggerStepThrough]
        public static string ToString<T>(this IEnumerable<T> item, string separator)
        {
            var result = string.Empty;
            foreach (var s in item) result += s + separator;
            result = result.TrimEnd(separator.ToCharArray());
            return result;
        }

        [System.Diagnostics.DebuggerStepThrough]
        public static List<T> ToList<T>(this int item, Func<int, T> populateFunction)
        {
            List<T> result = new List<T>();
            for (int i = 0; i < item; i++) result.Add(populateFunction(i));
            return result;
        }

        [System.Diagnostics.DebuggerStepThrough]
        public static dynamic ToExpando(this object o, bool excludeReadOnlyProperties = false)
        {
            var result = new ExpandoObject();
            var d = result as IDictionary<string, object>; //work with the Expando as a Dictionary
            if (o.GetType() == typeof(ExpandoObject)) return o; //shouldn't have to... but just in case
            if (o.GetType() == typeof(NameValueCollection))
            {
                var nv = (NameValueCollection)o;
                nv.Cast<string>().Select(key => new KeyValuePair<string, object>(key, nv[key])).ToList().ForEach(i => d.Add(i));
            }
            else
            {
                var props = o.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(x => !excludeReadOnlyProperties || x.CanWrite);

                foreach (var item in props)
                {
                    d.Add(item.Name, item.GetValue(o, null));
                }
            }
            return result;
        }
        /// <summary>
        /// Turns the object into a Dictionary
        /// </summary>
        [System.Diagnostics.DebuggerStepThrough]
        public static IDictionary<string, object> ToDictionary(this object thingy, bool excludeReadOnlyProperties = false)
        {
            return (IDictionary<string, object>)thingy.ToExpando(excludeReadOnlyProperties);
        }


        public static object GetValue(this object o, string memberName)
        {
            object result = null;

            if (o is IDictionary)
            {
                var dict = (o as IDictionary);
                return dict.Contains(memberName) ? dict[memberName] : null;
            }

            var objectType = o.GetType();
            var pi = objectType.GetProperty(memberName);
            if (pi != null)
            {

                result = pi.GetValue(o, null);
                return result;
            }

            var fi = objectType.GetField(memberName);
            if (fi != null)
            {
                result = fi.GetValue(o);
            }

            return result;
        }

        public static bool SetValue(this object o, string memberName, object value)
        {
            var objectType = o.GetType();
            var pi = objectType.GetProperty(memberName);
            if ((pi != null) && (pi.CanWrite))
            {
                pi.SetValue(o, value, null);
                return true;
            }

            var fi = objectType.GetField(memberName, BindingFlags.NonPublic | BindingFlags.Instance);
            if (fi != null)
            {
                fi.SetValue(o, value);
                return true;
            }

            fi = objectType.GetField("_" + memberName, BindingFlags.NonPublic | BindingFlags.Instance);
            if (fi != null)
            {
                fi.SetValue(o, value);
                return true;
            }

            return false;
        }
    }
}
